#include "precompiled.h"
#include "OpenGLWindow.h"

OpenGLWindow::OpenGLWindow() :
	hWnd(NULL),
	timer(NULL)
{
	gsInstance = this;
}

void OpenGLWindow::InitializeWindow(
	HINSTANCE hInstance,
	const char *title,
	int x,
	int y,
	int width,
	int height)
{
	//initialize the window class
	WNDCLASSEX wcx;
	wcx.cbSize = sizeof(wcx);							//size of structure
	wcx.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;		//own device context for openGL
	wcx.lpfnWndProc = StaticWndProc;					//window process callback
	wcx.cbClsExtra = 0;									//no extra memory
	wcx.cbWndExtra = 0;
	wcx.hInstance = hInstance;							//instance
	wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);		//icon
	wcx.hCursor = LoadCursor(NULL, IDC_ARROW);			//pointer
	wcx.hbrBackground = NULL;							//no background
	wcx.lpszMenuName = NULL;							//no menu
	wcx.lpszClassName = "OpenGLWindow";					//class name
	wcx.hIconSm = NULL;

	//register the window class
	if(!RegisterClassEx(&wcx)) {
		throw std::runtime_error("Failed To Register The Window Class.");
	}

	// Create The Window
	hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW | WS_EX_APPWINDOW,	// Extended Style For The Window
		"OpenGLWindow",						// Class Name
		title,								// Window Title
		WS_OVERLAPPEDWINDOW |				// Defined Window Style
		WS_CLIPSIBLINGS |					// Required Window Style
		WS_CLIPCHILDREN,					// Required Window Style
		x,
		y,
		width,
		height,								// Window Position
		NULL,								// No Parent Window
		NULL,								// No Menu
		hInstance,							// Instance
		NULL);								// Dont Pass Anything To WM_CREATE

	if(hWnd == 0) {
		throw std::runtime_error("Failed to create the window.");
	}

	HDC hDC = GetDC(hWnd);

	const PIXELFORMATDESCRIPTOR pfd =	{			// pfd Tells Windows How We Want Things To Be
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		24,											// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		8,											// bits of Alpha
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		32,											// 32Bit Z-Buffer (Depth Buffer)  
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		0,											// Ignored
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};

	//set up the GL context using wgl calls
	GLuint PixelFormat = ChoosePixelFormat(hDC, &pfd);
	if(PixelFormat == 0) {		// Did Windows Find A Matching Pixel Format?
		throw std::runtime_error("Can't Find A Suitable PixelFormat.");
	}

	if(!SetPixelFormat(hDC, PixelFormat, &pfd)) {		// Are We Able To Set The Pixel Format?
		throw std::runtime_error("Can't Set The PixelFormat.");
	}

	HGLRC hRC = wglCreateContext(hDC);
	if(hRC == 0) {								// Are We Able To Get A Rendering Context?
		throw std::runtime_error("Can't Create A GL Rendering Context.");
	}

	if(!wglMakeCurrent(hDC, hRC)) {					// Try To Activate The Rendering Context
		throw std::runtime_error("Can't Activate The GL Rendering Context.");
	}
}

// deltaT is the time between frames in ms
WPARAM OpenGLWindow::ShowWindow(unsigned int deltaT, int nCmdShow) {		//show this window
	if(::ShowWindow(hWnd, nCmdShow) != 0) {
		throw std::runtime_error("Could not show window");
	}
	if(deltaT != 0) {
		timer = SetTimer(hWnd, 0, deltaT, 0);	// TODO: fix
		if(timer == 0) {
			throw std::runtime_error("Could not set timer.");
		}
	}

	MSG		msg;		// Windows Message Structure
	BOOL	ret = -1;		// Return value from GetMessage
	
	while((ret = GetMessage(&msg, hWnd, 0, 0)) != 0) {	//get a message from the queue
		if (ret == -1) {
			return -1;
		} else {
			TranslateMessage(&msg);				// Translate The Message
			DispatchMessage(&msg);				// Dispatch The Message
		}
	}

	return msg.wParam;
}


LRESULT OpenGLWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	HDC hDC;
	HGLRC hRC;
	
	// Check For Windows Messages
	switch (uMsg) {
	case WM_KEYDOWN:
		OnKeyDown((char)GET_KEYSTATE_WPARAM(wParam));
		break;
	case WM_TIMER:
		OnUpdate();
	case WM_PAINT:
		PAINTSTRUCT ps;
		hDC = BeginPaint(hWnd, &ps);
		OnDisplay();
		SwapBuffers(hDC);
		EndPaint(hWnd, &ps);
		break;
	case WM_SIZE:
		OnResize((GLsizei)LOWORD(lParam), (GLsizei)HIWORD(lParam));
		break;
	case WM_DESTROY:								// Did We Receive A Close Message?
		if(timer != NULL) {
			KillTimer(hWnd, timer);
		}
		OnDestroy();
		hRC = wglGetCurrentContext();
		hDC = wglGetCurrentDC();
		wglMakeCurrent(0, 0);
		ReleaseDC(hWnd, hDC);
		wglDeleteContext(hRC);
		PostQuitMessage(0);						// Send A Quit Message
		break;									// Jump Back
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam); // Pass All Unhandled Messages To DefWindowProc
}

LRESULT CALLBACK OpenGLWindow::StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	return gsInstance->WndProc(hWnd, uMsg, wParam, lParam);
}

OpenGLWindow *OpenGLWindow::gsInstance = 0;